﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;

namespace SharedGenomics.Core
{
    public static class EventHandlerExtensions
    {
        public static void Raise(this EventHandler handler, object sender, EventArgs e)
        {
            if (handler != null)
                handler(sender, e);
        }

        public static void Raise(this EventHandler handler, object sender)
        {
            if (handler != null)
                handler(sender, EventArgs.Empty);
        }

        public static void Raise<T>(this EventHandler<T> handler, object sender, T e) where T : EventArgs
        {
            if (handler != null)
                handler(sender, e);
        }
    }

    public static class StringExtensions
    {
        public static bool IsEmptyOrWhitespace(this string value)
        {
            return value == null || value.Trim().Length == 0;
        }

        public static string EnsureTrailingDirectorySeparator(this string path)
        {
            string separator = @"\";
            if (path == null)
                return separator;
            
            return path.EndsWith(separator) ? path : path + separator;
        }

        public static System.Security.SecureString ToSecureString(this string str)
        {
            if (str == null)
                return null;

            System.Security.SecureString secString = new System.Security.SecureString();
            foreach (char c in str)
                secString.AppendChar(c);

            return secString;
        }

        public static Nullable<T> SafeConvert<T>(this string str) where T : struct
        {
            if (str == null) return null;

            Type targetType = typeof(T);
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(targetType);

            //if the type cannot be converted
            if (!converter.CanConvertFrom(typeof(string))) return null;

            try
            {
                return (T)converter.ConvertFrom(str);
            }
            catch (Exception)
            {
                //conversion failed so return null
                return null;
            }
        }
    }

    public static class ComparableExtensions
    {
        public static bool IsBetween<T>(this IComparable<T> comp, T min, T max, bool inclusive)
        {
            int minThreshold = inclusive ? 0 : 1;
            int maxThreshold = inclusive ? 0 : -1;

            return comp.CompareTo(min) >= minThreshold && comp.CompareTo(max) <= maxThreshold;
        }
    }

    public static class NullableExtensions
    {
        public static Nullable<TOut> SafeTransform<TSource, TOut>(this Nullable<TSource> source, Func<TSource, TOut> transform) where TOut : struct where TSource : struct
        {
            transform.ThrowIfNull("transform");
            return source.HasValue
                ? transform(source.Value)
                : (Nullable<TOut>)null;
        }
    }

    public static class ObjectExtensions
    {
        public static object GetPropertyValue(this object o, string propertyName)
        {
            o.ThrowIfNull("o");
            propertyName.ThrowIfNull("propertyName");

            Type targetType = o.GetType();

            try
            {
                PropertyInfo pi = targetType.GetProperty(propertyName);

                pi.ThrowIfNull<ArgumentException>(String.Format("Type {0} does not contain a property {1}", targetType.Name, propertyName));
                return pi.GetValue(o, null);
            }
            catch (AmbiguousMatchException)
            {
                //more than one property with the given name was found (should never happen?)
                throw new ArgumentException(String.Format("Type {0} contains more than one property called {1}", targetType.Name, propertyName));
            }
        }

        public static TValue GetValueSafe<T, TValue>(this T obj, Func<T, TValue> accessor)
        {
            accessor.ThrowIfNull("accessor");

            return obj == null
                ? default(TValue)
                : accessor(obj);
        }

        public static IEnumerable<T> AsSingleton<T>(this T obj)
        {
            yield return obj;
        }
    }

    public static class GenericExtensions
    {
        public static bool In<T>(this T obj, params T[] candidates)
        {
            return obj.In((IEnumerable<T>)candidates);
        }

        public static bool In<T>(this T obj, IEnumerable<T> candidates)
        {
            obj.ThrowIfNull("obj");
            return (candidates ?? Enumerable.Empty<T>()).Contains(obj);
        }

        public static IEnumerable<T> TakeAtIndices<T>(this IEnumerable<T> source, IEnumerable<int> indices)
        {
            source.ThrowIfNull("source");
            HashSet<int> indexSet = new HashSet<int>(indices ?? Enumerable.Empty<int>());
            return source.Where((item, index) => indexSet.Contains(index));
        }
    }

    public static class EnumerableExtensions
    {
        public static void ForEach<T>(this IEnumerable<T> items, Action<T> act)
        {
            foreach (T item in (items ?? Enumerable.Empty<T>()))
            {
                act(item);
            }
        }
    }

    public static class IntExtensions
    {
        public static void Times(this int count, Action act)
        {
            for (int i = 0; i < count; i++)
            {
                act();
            }
        }

        public static void Times(this int count, Action<int> act)
        {
            if (count > 0)
            {
                Enumerable.Range(0, count).ForEach(act);
            }
        }

        public static IEnumerable<T> Of<T>(this int count, Func<int, T> generatorFunc)
        {
            for (int i = 0; i < count; i++)
            {
                yield return generatorFunc(i);
            }
        }
    }

    public static class DictionaryExtensions
    {
        public static TValue GetValueWithDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
        {
            return dict.ContainsKey(key) ? dict[key] : default(TValue);
        }

        public static TValue GetValueForSafe<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, Func<TValue> missingValueFunc)
        {
            dict.ThrowIfNull("dict");

            if (!dict.ContainsKey(key))
            {
                dict[key] = missingValueFunc();
            }

            Debug.Assert(dict.ContainsKey(key));
            return dict[key];
        }
    }

    public static class QueueExtensions
    {
        public static IEnumerable<T> Dequeue<T>(this Queue<T> queue, int count)
        {
            queue.ThrowIfNull("queue");

            int dequeueCount = Math.Min(queue.Count, count);        //do not try to dequeue more items than are in the queue
            return dequeueCount.Of(i => queue.Dequeue());
        }

        public static IEnumerable<T> DequeueAll<T>(this Queue<T> queue)
        {
            queue.ThrowIfNull("queue");
            return queue.Dequeue(queue.Count);
        }
    }
}
